﻿using System;
using System.Collections.Generic;

namespace Sustainalytics.Utils
{
    public class IndicatorCodeComparer : IComparer<string>
    {
        private const char _splitChar = '.';
        private const string _eventIndicatorCode = "EV";
        private const string _corporateGovernanceCode = "CG";
        private static readonly IList<string> _themeOrder = new List<string> { "E", "S", "G", _eventIndicatorCode, _corporateGovernanceCode };

        public int Compare(string indicatorCodeA, string indicatorCodeB)
        {
            if (string.Equals(indicatorCodeA, indicatorCodeB)) return 0;

            var indicatorPartsA = TrimPrefixAndSplitIntoParts(indicatorCodeA);
            var indicatorPartsB = TrimPrefixAndSplitIntoParts(indicatorCodeB);

            var themeCompareResult = CompareThemes(indicatorPartsA[0], indicatorPartsB[0]);
            if (themeCompareResult != 0) return themeCompareResult; // eg S.12.2 is before G.1.1

            var partIndex = 1;

            while (indicatorPartsA.Length > partIndex && indicatorPartsB.Length > partIndex)
            {
                var partA = indicatorPartsA[partIndex];
                var partB = indicatorPartsB[partIndex];

                if (!string.Equals(partA, partB))
                {
                    // invalid cast exceptions will bubble up
                    var castedPartA = int.Parse(partA);
                    var castedPartB = int.Parse(partB);

                    return castedPartA.CompareTo(castedPartB); // eg. S.1.2.2 is before S.1.11.1
                }

                partIndex++;
            }

            return indicatorPartsA.Length.CompareTo(indicatorPartsB.Length); // eg. E.1.1.1 is before E.1.1.1.1
        }

        private static string[] TrimPrefixAndSplitIntoParts(string indicatorCode) => 
            SplitIntoParts(indicatorCode);

        private static string[] SplitIntoParts(string indicatorCode)
        {
            var indicatorParts = indicatorCode.Split(_splitChar);
            if (indicatorParts.Length >= 3 || IsValidEventIndicator(indicatorParts) || IsValidGovernanceIndicator(indicatorParts))
                return indicatorParts;

            throw new ArgumentException($"Unexpected Indicator Code {indicatorCode}");
        }

        private static bool IsValidGovernanceIndicator(string[] indicatorParts) => 
            indicatorParts.Length >= 2 && indicatorParts[0] == _corporateGovernanceCode;

        private static bool IsValidEventIndicator(string[] indicatorParts) => 
            indicatorParts.Length >= 2 && indicatorParts[0] == _eventIndicatorCode;

        private static int CompareThemes(string themeA, string themeB)
        {
            var indexThemeA = _themeOrder.IndexOf(themeA);
            var indexThemeB = _themeOrder.IndexOf(themeB);

            if (indexThemeA == -1)
                throw new ArgumentException($"Unexpected Theme Code {themeA}");
            if (indexThemeB == -1)
                throw new ArgumentException($"Unexpected Theme Code {themeB}");

            return indexThemeA.CompareTo(indexThemeB);
        }
    }
}
